package com.franklin.java.api.psi.data.resolver;

import com.franklin.java.api.psi.data.PsiData;
import com.franklin.java.utils.PsiUtils;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.franklin.plugins.common.utils.PatternUtils;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaFile;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;

/**
 * PsiData解析器
 * @author Franklin
 * @since 2021/08/15/ 21:04
 */
interface PsiDataResolver {

    /**
     * 解析变量为PsiData
     * @param name
     * @param value
     * @param dataElement
     * @return
     */
    PsiData resolve(String name, String value, PsiElement dataElement);

    String VARIABLE_PATTERN
            = "^\\s*(?<variableType>.+)\\s*%s\\s*(=\\s*(?<variableValue>.+))*\\s*;[\\s\\S]*$";

    String GENERIC_TYPE_PATTERN
            = "^\\s*(?<variableType>\\w*)\\s*(?<genericAll><\\s*(?<genericType>.*)\\s*>\\s*)?[\\s\\S]*$";

    String IMPLEMENT_PATTERN
        = "^\\s*new\\s+(?<implementType>.*)\\s*\\([\\s\\S]*\\)\\s*$";

    @Nullable
    default PsiData resolveVariable(PsiElement originElement, String value, List<? extends PsiElement> psiElementList) {
        PsiElement psiElement = null;
        String val = value;
        String type = "String";
        String typeFullName = "java.lang.String";
        if (DataCheckUtils.isNotEmpty(psiElementList)) {
            String regex = String.format(VARIABLE_PATTERN, value);
            for (PsiElement element : psiElementList) {
                if (!isSuitableElement(element,value)){
                    continue;
                }
                String statementText = element.getText();
                Map<String, String> findResult = PatternUtils.find(regex, statementText, "variableType", "variableValue");
                String defaultType = getVariableDefaultType(element);
                String variableType = findResult.getOrDefault("variableType", defaultType);
                String variableValue = findResult.getOrDefault("variableValue", "");
                if (DataCheckUtils.isNotEmpty(variableType)) {
                    psiElement = element;
                    val = variableValue;
                    Map<String, String> implementTypeResult = PatternUtils.find(IMPLEMENT_PATTERN, variableValue, "implementType");
                    String implementType = implementTypeResult.getOrDefault("implementType", "");
                    if (DataCheckUtils.isNotEmpty(implementType)) {
                        type = implementType;
                        //判断泛型
                        Map<String, String> genericResult = PatternUtils.find(GENERIC_TYPE_PATTERN, implementType, "variableType", "genericAll","genericType");
                        if (DataCheckUtils.isNotEmpty(genericResult)){
                            String implementBasicType = genericResult.get("variableType");
                            String genericAll = genericResult.getOrDefault("genericAll", "<>");
                            if (StringUtils.deleteWhitespace(genericAll).equals("<>")){
                                type = variableType;
                            }
                        }
                    } else {
                        type = variableType;
                    }
                    typeFullName = PsiUtils.findTypeFullName((PsiJavaFile) originElement.getContainingFile(), type);
                    type = PsiUtils.getSimpleClassName(type);
                    return new PsiData(type,val,psiElement,typeFullName, originElement);
                }
            }
        }
        return null;
    }

    /**
     * 获取默认类型<br>
     * @author Franklin
     * @since 2021/8/19 11:03
     * @param psiElement:
     * @return java.lang.String
     */
    default String getVariableDefaultType(PsiElement psiElement){
        return "";
    }

    /**
     * 判断是否是需要判断的PsiElement<br>
     * @author Franklin
     * @since 2021/8/19 11:03
     * @param psiElement:
     * @param targetValue:
     * @return boolean
     */
    default boolean isSuitableElement(PsiElement psiElement,String targetValue){
        return true;
    }
}
